package com.wang.di.core.bean;

import com.wang.di.core.exception.BeanInitException;
import com.wang.di.core.handler.AnnoHandler;
import com.wang.di.core.handler.JustMethodProxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public abstract class BaseBean {
    private static Set<Class<? extends Annotation>> BASE_ANNO = new HashSet<>();

    static {
        BASE_ANNO.add(Documented.class);
        BASE_ANNO.add(Inherited.class);
        BASE_ANNO.add(Native.class);
        BASE_ANNO.add(Repeatable.class);
        BASE_ANNO.add(Target.class);
        BASE_ANNO.add(Retention.class);
    }


    private Enhancer enhancer;
    private Map<Method, List<JustMethodProxy>> proxyMethodMap = new HashMap<>();

    protected Class clz;
    // 类的所有注解
    protected List<? extends Annotation> annoList;

    public BaseBean(Class clz, Annotation anno) {
        this.clz = clz;
        this.annoList = allAnno(clz);
        if (clz.isInterface()) {
            // 接口类，不使用cglib
            return;
        }

        enhancer = new Enhancer();
        enhancer.setSuperclass(clz);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            List<JustMethodProxy> list = this.proxyMethodMap.getOrDefault(method, new ArrayList<>());
            for (JustMethodProxy callback : list) {
                callback.pre(method, BaseBean.this.clz, o, objects);
            }
            Object result = methodProxy.invokeSuper(o, objects);
            for (JustMethodProxy callback : list) {
                result = callback.after(method, BaseBean.this.clz, o, objects, result);
            }
            return result;
        });
    }

    // 判断是否符合条件的bean实例
    public abstract boolean check(Object... cond);

    public Class getClz() {
        return clz;
    }

    // bean生命周期控制函数
    public abstract Object get() throws BeanInitException;

    // 创建实例
    protected Object create() throws BeanInitException {
        try {
            Object instance = enhancer.create();
            Field[] fs = clz.getDeclaredFields();
            Method[] methods = clz.getDeclaredMethods();

            for (Field f : fs) {
                List<Annotation> list = new LinkedList<>();
                for (Annotation anno : f.getAnnotations()) {
                    list.add(anno);
                    list.addAll(this.allAnno(anno.annotationType()));
                }
                for (Annotation anno : list) {
                    AnnoHandler handler = AnnoHandler.HANDLER_MAP.get(anno.annotationType());
                    if (handler == null) continue;
                    Object objField = handler.field(instance, this.clz, f, anno);
                    f.setAccessible(true);
                    f.set(instance, objField);
                }
            }

            for (Method method : methods) {
                List<Annotation> list = new LinkedList<>();
                for (Annotation anno : method.getAnnotations()) {
                    list.add(anno);
                    list.addAll(this.allAnno(anno.annotationType()));
                }
                for (Annotation anno : list) {
                    AnnoHandler handler = AnnoHandler.HANDLER_MAP.get(anno.annotationType());
                    if (handler == null) continue;
                    JustMethodProxy justMethodProxy = handler.method(instance, clz, method, anno);
                    if (justMethodProxy == null) continue;
                    List<JustMethodProxy> proxyList = this.proxyMethodMap.computeIfAbsent(method, k -> new ArrayList<>());
                    proxyList.add(handler.method(instance, clz, method, anno));
                }
            }

            boolean isInitializing = Initializing.class.isAssignableFrom(this.clz);
            if (isInitializing) {
                ((Initializing) instance).initialize();
            }
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BeanInitException();
        }

    }

    // 递归获取所有注解
    private List<Annotation> allAnno(Class clz) {
        List<Annotation> list = new LinkedList<>();
        for (Annotation anno : clz.getAnnotations()) {
            if (BASE_ANNO.contains(anno.annotationType())) continue;
            List<Annotation> subList = allAnno(anno.getClass());
            list.addAll(subList);
        }
        return list;
    }
}
